package com.gzc.just.play.last.war.common.share.persistence;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * Base data access implementation class
 */
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> {

    private static final Logger log = LoggerFactory.getLogger(BaseRepositoryImpl.class);

    private final EntityManager entityManager;
    private final JpaEntityInformation<T, ?> entityInformation;
    
    public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation.getJavaType(), entityManager);
        this.entityManager = entityManager;
        this.entityInformation = entityInformation;
    }
    
    /**
     * Find all and sort by ID
     */
    @Override
    @Transactional(readOnly = true)
    public List<T> findAllByOrderByIdAsc() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> query = cb.createQuery(getDomainClass());
        Root<T> root = query.from(getDomainClass());
        query.orderBy(cb.asc(root.get("id")));
        
        TypedQuery<T> typedQuery = entityManager.createQuery(query);
        return typedQuery.getResultList();
    }
    
    /**
     * Find by condition with pagination
     */
    @Override
    @Transactional(readOnly = true)
    public Page<T> findByCondition(String condition, Pageable pageable) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> query = cb.createQuery(getDomainClass());
        Root<T> root = query.from(getDomainClass());
        
        // Here simply handle, actual application should parse condition and construct criteria
        if (condition != null && !condition.isEmpty()) {
            // Example: assuming condition is "fieldName:value" format
            String[] parts = condition.split(":");
            if (parts.length == 2) {
                String fieldName = parts[0];
                String value = parts[1];
                
                try {
                    // Try to convert value to appropriate type
                    Object convertedValue = convertValue(value, root.get(fieldName).getJavaType());
                    query.where(cb.equal(root.get(fieldName), convertedValue));
                } catch (Exception e) {
                    log.error("Failed to parse condition: {}", condition, e);
                }
            }
        }
        
        TypedQuery<T> typedQuery = entityManager.createQuery(query);
        
        // Pagination settings
        if (pageable.isPaged()) {
            typedQuery.setFirstResult((int) pageable.getOffset());
            typedQuery.setMaxResults(pageable.getPageSize());
        }
        
        List<T> content = typedQuery.getResultList();
        
        // Get total count
        CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
        Root<T> countRoot = countQuery.from(getDomainClass());
        countQuery.select(cb.count(countRoot));
        
        // Apply same condition
        if (condition != null && !condition.isEmpty()) {
            String[] parts = condition.split(":");
            if (parts.length == 2) {
                String fieldName = parts[0];
                String value = parts[1];
                
                try {
                    Object convertedValue = convertValue(value, countRoot.get(fieldName).getJavaType());
                    countQuery.where(cb.equal(countRoot.get(fieldName), convertedValue));
                } catch (Exception e) {
                    log.error("Failed to parse condition for count: {}", condition, e);
                }
            }
        }
        
        Long total = entityManager.createQuery(countQuery).getSingleResult();
        
        return new PageImpl<>(content, pageable, total);
    }
    
    /**
     * Batch insert
     */
    @Override
    @Transactional
    public List<T> batchInsert(List<T> entities) {
        if (entities == null || entities.isEmpty()) {
            return new ArrayList<>();
        }
        
        List<T> result = new ArrayList<>();
        int batchSize = 50; // Batch size
        int i = 0;
        
        for (T entity : entities) {
            entityManager.persist(entity);
            result.add(entity);
            
            i++;
            if (i % batchSize == 0) {
                // Refresh batch
                entityManager.flush();
                entityManager.clear();
            }
        }
        
        // Refresh remaining entities
        entityManager.flush();
        entityManager.clear();
        
        return result;
    }
    
    /**
     * Batch update
     */
    @Override
    @Transactional
    public List<T> batchUpdate(List<T> entities) {
        if (entities == null || entities.isEmpty()) {
            return new ArrayList<>();
        }
        
        List<T> result = new ArrayList<>();
        int batchSize = 50; // Batch size
        int i = 0;
        
        for (T entity : entities) {
            T mergedEntity = entityManager.merge(entity);
            result.add(mergedEntity);
            
            i++;
            if (i % batchSize == 0) {
                // Refresh batch
                entityManager.flush();
                entityManager.clear();
            }
        }
        
        // Refresh remaining entities
        entityManager.flush();
        entityManager.clear();
        
        return result;
    }
    
    /**
     * Soft delete (if entity supports)
     */
    @Override
    @Transactional
    public int softDeleteById(ID id) {
        // Check if entity supports soft delete
        if (!BaseEntity.class.isAssignableFrom(getDomainClass())) {
            log.warn("Entity {} does not support soft delete", getDomainClass().getSimpleName());
            return 0;
        }
        
        try {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            var update = cb.createCriteriaUpdate(getDomainClass());
            Root<T> root = update.from(getDomainClass());
            
            update.set(root.get("deleted"), true);
            update.set(root.get("updateTime"), System.currentTimeMillis());
            update.where(cb.equal(root.get("id"), id));
            
            return entityManager.createQuery(update).executeUpdate();
        } catch (Exception e) {
            log.error("Failed to soft delete entity with id: {}", id, e);
            return 0;
        }
    }
    
    /**
     * Soft delete (if entity supports)
     */
    @Override
    @Transactional
    public int softDeleteByIds(Iterable<ID> ids) {
        // Check if entity supports soft delete
        if (!BaseEntity.class.isAssignableFrom(getDomainClass())) {
            log.warn("Entity {} does not support soft delete", getDomainClass().getSimpleName());
            return 0;
        }
        
        try {
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            var update = cb.createCriteriaUpdate(getDomainClass());
            Root<T> root = update.from(getDomainClass());
            
            update.set(root.get("deleted"), true);
            update.set(root.get("updateTime"), System.currentTimeMillis());
            update.where(root.get("id").in(ids));
            
            return entityManager.createQuery(update).executeUpdate();
        } catch (Exception e) {
            log.error("Failed to soft delete entities", e);
            return 0;
        }
    }
    
    /**
     * Find by condition
     */
    @Override
    @Transactional(readOnly = true)
    public List<T> findByCustomCondition(String condition) {
        // Here simply handle, actual application should dynamically construct based on condition
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<T> query = cb.createQuery(getDomainClass());
        Root<T> root = query.from(getDomainClass());
        
        // Apply condition (simplified example)
        if (condition != null && !condition.isEmpty()) {
            // Actual implementation should duplicate, support multiple condition formats
            // Here just as example
            try {
                // Assuming condition is part of JPQL
                String jpql = "select e from " + getDomainClass().getSimpleName() + " e where " + condition;
                return entityManager.createQuery(jpql, getDomainClass()).getResultList();
            } catch (Exception e) {
                log.error("Failed to parse custom condition: {}", condition, e);
            }
        }
        
        return entityManager.createQuery(query).getResultList();
    }
    
    /**
     * Count all
     */
    @Override
    @Transactional(readOnly = true)
    public long countAll() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> query = cb.createQuery(Long.class);
        Root<T> root = query.from(getDomainClass());
        query.select(cb.count(root));
        
        return entityManager.createQuery(query).getSingleResult();
    }
    
    /**
     * ---- CacheableRepository default implementations ----
     */
    @Override
    public Optional<T> findByIdCached(ID id) {
        return findById(id);
    }

    @Override
    public List<T> findAllCached() {
        return findAll();
    }

    @Override
    public List<T> findAllByIdCached(Iterable<ID> ids) {
        return findAllById(ids);
    }

    @Override
    public <S extends T> S saveAndCache(S entity) {
        return save(entity);
    }

    @Override
    public <S extends T> List<S> saveAllAndCache(Iterable<S> entities) {
        return saveAll(entities);
    }

    @Override
    public void deleteByIdAndEvictCache(ID id) {
        deleteById(id);
    }

    @Override
    public void deleteAndEvictCache(T entity) {
        delete(entity);
    }

    @Override
    public void deleteAllAndEvictCache(Iterable<? extends T> entities) {
        deleteAll(entities);
    }

    @Override
    public void deleteAllAndEvictCache() {
        deleteAll();
    }

    /**
     * Convert value type
     */
    private Object convertValue(String value, Class<?> targetType) {
        if (targetType == String.class) {
            return value;
        } else if (targetType == Integer.class || targetType == int.class) {
            return Integer.parseInt(value);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.parseLong(value);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            return Boolean.parseBoolean(value);
        } else if (targetType == Double.class || targetType == double.class) {
            return Double.parseDouble(value);
        } else if (targetType == Float.class || targetType == float.class) {
            return Float.parseFloat(value);
        } else {
            // Default to string, let JPA handle conversion
            return value;
        }
    }
}